package com.katesoft.scale4j.web.support;

import com.katesoft.scale4j.persistent.client.LocalHibernateDaoSupport;
import com.katesoft.scale4j.web.exceptions.LocaleNotFoundException;
import com.katesoft.scale4j.web.exceptions.RoleNotFoundException;
import com.katesoft.scale4j.web.model.Role;
import com.katesoft.scale4j.web.model.User;
import com.katesoft.scale4j.web.model.UserLocale;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import static com.katesoft.scale4j.common.utils.AssertUtility.assertNotNull;

/**
 * This class is placeholder of
 * {@link com.katesoft.scale4j.persistent.client.LocalHibernateTemplate} and default implementation
 * of IWebappSupportOperations interface.
 * <p/>
 * Convenient super class for Hibernate-based data access objects within application services.
 * 
 * @author kate2007
 */
public class WebappDaoSupport extends LocalHibernateDaoSupport implements IWebappSupportOperations,
         UserDetailsService {
   /*
    * (non-Javadoc)
    * 
    * @see com.katesoft.scale4j.persistent.spring.IWebappSupportOperations #getLoggedInUser()
    */
   @Override
   public User getLoggedInUser() {
      Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
      if (authentication == null) {
         return null;
      }
      return loadUserByUsername(authentication.getName());
   }

   /*
    * (non-Javadoc)
    * 
    * @see com.katesoft.scale4j.persistent.spring.IWebappSupportOperations
    * #loadUser(com.katesoft.scale4j.web.security.IUserDetails)
    */
   @Override
   public User loadUser(String username) {
      User user = username != null ? loadUserByUsername(username) : getLoggedInUser();
      if (user == null) {
         throw new UsernameNotFoundException("User is not bound to thread");
      }
      return user;
   }

   /*
    * (non-Javadoc)
    * 
    * @see com.katesoft.scale4j.persistent.spring.IWebappSupportOperations
    * #loadUserLocale(java.lang.String)
    */
   @Override
   public UserLocale loadLocale(final String code) {
      assertNotNull(code, "ILocale code can't be null");
      return getLocalHibernateTemplate().execute(new HibernateCallback<UserLocale>() {
         @Override
         public UserLocale doInHibernate(final Session session) throws HibernateException {
            Criteria criteria = session.createCriteria(UserLocale.class);
            criteria.add(Restrictions.eq(UserLocale.PROP_CODE, code));
            UserLocale locale = (UserLocale) criteria.uniqueResult();
            if (locale == null) {
               throw new LocaleNotFoundException(code);
            }
            Hibernate.initialize(locale);
            return locale;
         }
      });
   }

   @Override
   public User loadUserByUsername(final String username) throws UsernameNotFoundException,
            DataAccessException {
      assertNotNull(username, "username can't be null");
      return getLocalHibernateTemplate().execute(new HibernateCallback<User>() {
         @Override
         public User doInHibernate(final Session session) throws HibernateException {
            Criteria criteria = session.createCriteria(User.class);
            criteria.add(Restrictions.eq(User.PROP_LOGIN, username));
            User u = (User) criteria.uniqueResult();
            if (u == null) {
               throw new UsernameNotFoundException(username);
            }
            return initializeUserProxy(u);
         }
      });
   }

   /*
    * (non-Javadoc)
    * 
    * @see com.katesoft.scale4j.persistent.spring.IWebappSupportOperations
    * #loadRoleByName(java.lang.String)
    */
   @Override
   public Role loadRoleByName(final String role) {
      assertNotNull(role, "role can't be null");
      return getLocalHibernateTemplate().execute(new HibernateCallback<Role>() {
         @Override
         public Role doInHibernate(Session session) throws HibernateException {
            Criteria criteria = session.createCriteria(Role.class);
            criteria.add(Restrictions.eq(Role.PROP_ROLE, role));
            Role r = (Role) criteria.uniqueResult();
            if (r == null) {
               throw new RoleNotFoundException(role);
            }
            Hibernate.initialize(r);
            return r;
         }
      });
   }

   /**
    * forces dirty load of user as well as user roles.
    * 
    * @param user
    *           target
    * @return fully loaded user with loaded relations.
    */
   protected User initializeUserProxy(User user) {
      assertNotNull(user, "User param can't not be null");
      user.forceAttributesLoad();
      if (user.getRoles() != null) {
         for (Role role : user.getRoles()) {
            Hibernate.initialize(role);
         }
      }
      return user;
   }
}
